package com.yss.sofa.report.engine.core;

import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.DeserializationConfig.Feature;
import org.codehaus.jackson.map.ObjectMapper;

import com.yss.sofa.report.engine.Activator;
import com.yss.sofa.report.engine.Const;
import com.yss.sofa.report.engine.DataSetData;
import com.yss.sofa.report.engine.DataSetData.TreeNode;
import com.yss.sofa.report.engine.Report;
import com.yss.sofa.report.engine.cell.FloatingCell;
import com.yss.sofa.report.engine.cell.ReportCell;
import com.yss.sofa.report.engine.cell.invisible.dataset.HeterogeneousSqlDataSetCell;
import com.yss.sofa.report.engine.cell.invisible.dataset.OsgiDataSetCell;
import com.yss.sofa.report.engine.cell.invisible.dataset.ScriptDataSetCell;
import com.yss.sofa.report.engine.cell.invisible.dataset.SqlDataSetCell;
import com.yss.sofa.report.engine.cell.invisible.dataset.StaticDataSetCell;
import com.yss.sofa.report.engine.cell.invisible.dataset.VirtualDataSetCell;
import com.yss.sofa.report.engine.cell.invisible.param.ParameterCell;
import com.yss.sofa.report.engine.cell.meta.MetaCell;
import com.yss.sofa.report.engine.cell.page.PageCell;
import com.yss.sofa.report.engine.cell.page.header.ColCell;
import com.yss.sofa.report.engine.cell.page.header.RowCell;
import com.yss.sofa.report.engine.cell.visible.SimpleCell;
import com.yss.sofa.report.engine.cell.visible.VisibleCell;
import com.yss.sofa.report.engine.cell.visible.extendable.ChartCell;
import com.yss.sofa.report.engine.cell.visible.extendable.DataCell;
import com.yss.sofa.report.engine.cell.visible.extendable.ExpressionCell;
import com.yss.sofa.report.engine.cell.visible.extendable.FieldCell;
import com.yss.sofa.report.engine.cell.visible.extendable.StaticCell;
import com.yss.sofa.report.engine.cell.visible.form.CheckBoxFormCell;
import com.yss.sofa.report.engine.cell.visible.form.CustomFormCell;
import com.yss.sofa.report.engine.cell.visible.form.DataSetListFormCell;
import com.yss.sofa.report.engine.cell.visible.form.DataSetTreeFormCell;
import com.yss.sofa.report.engine.cell.visible.form.DateFormCell;
import com.yss.sofa.report.engine.cell.visible.form.FormCell;
import com.yss.sofa.report.engine.cell.visible.form.NumberFormCell;
import com.yss.sofa.report.engine.cell.visible.form.UrlListFormCell;
import com.yss.sofa.report.engine.cell.visible.form.UrlTreeFormCell;
import com.yss.sofa.report.engine.cell.visible.form.UrlTreeSelectFormCell;
import com.yss.sofa.report.engine.cell.visible.result.ChartResultCell;
import com.yss.sofa.report.engine.cell.visible.result.FormResultCell;
import com.yss.sofa.report.engine.cell.visible.result.NormalResultCell;
import com.yss.sofa.report.engine.cell.visible.result.ResultCell;
import com.yss.sofa.report.engine.poi.SheetUtil2;
import com.yss.sofa.report.engine.series.Series;
import com.yss.sofa.report.engine.util.CssUtil;
import com.yss.sofa.report.engine.util.ImageUtil;
import com.yss.sofa.report.engine.util.NumberUtil;
import com.yss.sofa.report.engine.util.RenderUtil;

/**
 * 变速器 起辅助作用
 * 
 * @author wangyh
 * @version 1.0, 2018-6-28
 * @since 1.0, 2018-6-28
 */
public final class Transmission {
	static final class Coordinate {
		private static final Pattern pattern=Pattern.compile("^[A-Z]+[1-9]+[0-9]*$");
		public int row;
		public int col;
		public Coordinate(int row,int col){
			this.row=row;
			this.col=col;
		}
		public Coordinate(String s) throws Exception{
			s=s.toUpperCase();
			if(!pattern.matcher(s).matches()){
				throw new Exception("["+s+"]不是有效的Excel坐标值");
			}
			
			int len=s.length();
			String row="";
			String col="";
			int index=0;
			while(index<len){
				char c=s.charAt(index);
				if(c>='A' && c<='Z'){
					col+=c;
					index++;
				} else {
					break;
				}
			}
			while(index<len){
				char c=s.charAt(index);
				if(c>='0' && c<='9'){
					row+=c;
					index++;
				} else {
					break;
				}
			}
			int x=NumberUtil.to10(col)-1;
			int y=Integer.valueOf(row)-1;
			this.row=y;
			this.col=x;
		}
	}
	private Transmission(){}
	static Coordinate getTrueRowColCount(Sheet sheet) throws Exception{
		int rowCount=sheet.getLastRowNum()+1;
		for(int r=0;r<rowCount;r++){
			Row row=sheet.getRow(r);
			if(row==null){
				row=sheet.createRow(r);
			}
			int colCount=row.getLastCellNum();
			for(int c=0;c<colCount;c++){
				Cell cell=row.getCell(c);
				if(cell==null){
					cell=row.createCell(c,CellType.BLANK);
				}
				String value=getExcelCellRawValue(cell);
				if(StringUtils.isNotEmpty(value) && value.startsWith("##")){
					return new Coordinate(r,c);
				}
			}
		}
		throw new Exception("sheet["+sheet.getSheetName()+"]没有以##结束");
	}
	static String getExcelCellRawValue(Cell cell){
		if(cell==null){
			return null;
		}
		CellType type=cell.getCellTypeEnum();
		String value=null;
		switch (type) {
		case BLANK:
			break;
		case BOOLEAN:
			value=Boolean.toString(cell.getBooleanCellValue());
			break;
		case ERROR:
			if(cell instanceof XSSFCell){
				value=((XSSFCell)cell).getErrorCellString();
			}else if(cell instanceof HSSFCell){
				value=cell.getErrorCellValue()+"";
			}
			break;
		case FORMULA:
			value="="+cell.getCellFormula();
			break;
		case NUMERIC:
			value=Double.toString(cell.getNumericCellValue());
			break;
		case STRING:
			value=cell.getStringCellValue();
			break;
		default:
			break;
		}
		return value;
	}
	static ReportCell convertToReportCell(int type,String value) throws Exception{
		switch (type) {
			case Integer.MIN_VALUE:{//模板中不需要明确指定meta.type属性，也不会读type属性，而是根据##来判断
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				MetaCell pc=mapper.readValue(value.substring(2), MetaCell.class);
				return pc;
			}
			case Integer.MAX_VALUE:{//模板中不需要明确指定page.type属性，也不会读type属性，而是根据##来判断
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				PageCell pc=mapper.readValue(value.substring(2), PageCell.class);
				return pc;
			}
			case Const.REPORT_CELL_TYPE_SIMPLE:{
				SimpleCell sc=new SimpleCell(value);
				return sc;
			}
			case Const.REPORT_CELL_TYPE_STATIC:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				StaticCell sc=mapper.readValue(value.substring(1), StaticCell.class);
				return sc;
			}
			case Const.REPORT_CELL_TYPE_SQL_DATASET:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				SqlDataSetCell dsc=mapper.readValue(value.substring(1), SqlDataSetCell.class);
				return dsc;
			}
			case Const.REPORT_CELL_TYPE_OSGI_DATASET:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				OsgiDataSetCell dsc=mapper.readValue(value.substring(1), OsgiDataSetCell.class);
				return dsc;
			}
			case Const.REPORT_CELL_TYPE_SCRIPT_DATASET:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				ScriptDataSetCell dsc=mapper.readValue(value.substring(1), ScriptDataSetCell.class);
				return dsc;
			}
			case Const.REPORT_CELL_TYPE_VIRTUAL_DATASET:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				mapper.configure(Feature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
				VirtualDataSetCell dsc=mapper.readValue(value.substring(1), VirtualDataSetCell.class);
				return dsc;
			}
			case Const.REPORT_CELL_TYPE_STATIC_DATASET:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				mapper.configure(Feature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
				StaticDataSetCell dsc=mapper.readValue(value.substring(1), StaticDataSetCell.class);
				return dsc;
			}
			case Const.REPORT_CELL_TYPE_HETEROGENEOUS_SQL_DATASET:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				mapper.configure(Feature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
				HeterogeneousSqlDataSetCell dsc=mapper.readValue(value.substring(1), HeterogeneousSqlDataSetCell.class);
				return dsc;
			}
			case Const.REPORT_CELL_TYPE_PARAM:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				ParameterCell pc=mapper.readValue(value.substring(1), ParameterCell.class);
				return pc;
			}
			case Const.REPORT_CELL_TYPE_DATA:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				DataCell dc=mapper.readValue(value.substring(1), DataCell.class);
				return dc;
			}
			case Const.REPORT_CELL_TYPE_EXP:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				ExpressionCell ec=mapper.readValue(value.substring(1), ExpressionCell.class);
				return ec;
			}
			case Const.REPORT_CELL_TYPE_CHART:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				ChartCell cc=mapper.readValue(value.substring(1), ChartCell.class);
				return cc;
			}
			case Const.REPORT_CELL_TYPE_FORM:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				FormCell fc=mapper.readValue(value.substring(1), FormCell.class);
				return fc;
			}
			case Const.REPORT_CELL_TYPE_FIELD:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				FieldCell fc=mapper.readValue(value.substring(1), FieldCell.class);
				return fc;
			}
			case Const.REPORT_CELL_TYPE_ROW_HEADER:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				RowCell rc=mapper.readValue(value.substring(1), RowCell.class);
				return rc;
			}
			case Const.REPORT_CELL_TYPE_COL_HEADER:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				ColCell cc=mapper.readValue(value.substring(1), ColCell.class);
				return cc;
			}
			case Const.REPORT_CELL_TYPE_FLOATING:{
				ObjectMapper mapper=new ObjectMapper();
				mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
				FloatingCell fc=mapper.readValue(value.substring(1), FloatingCell.class);
				return fc;
			}
			default:{
				throw new Exception("type["+type+"]不是有效的单元格类型");
			}
		}
	}
	static void initMetaSettings(Sheet sheet,MetaCell meta,int rowcount,int colcount) throws Exception{
		//before
		if(StringUtils.isNotEmpty(meta.getBefore())){
			String before=meta.getBefore();
			try {
				String value=getSpecifiedExcelCellRawValue(sheet,before);
				meta.setBefore(value);
			} catch (Exception e) {
				throw new Exception("无法解析sheet["+sheet.getSheetName()+"].meta[##].before["+before+"]的值",e);
			}
		}
		//after
		if(StringUtils.isNotEmpty(meta.getAfter())){
			String after=meta.getAfter();
			try {
				String value=getSpecifiedExcelCellRawValue(sheet,after);
				meta.setAfter(value);
			} catch (Exception e) {
				throw new Exception("无法解析sheet["+sheet.getSheetName()+"].meta[##].after["+after+"]的值",e);
			}
		}
	}
	static String getSpecifiedExcelCellRawValue(Sheet sheet,String coordinate) throws Exception{
		try {
			Coordinate c=new Coordinate(coordinate);
			Cell cell=sheet.getRow(c.row).getCell(c.col);
			String value=getExcelCellRawValue(cell);
			if(StringUtils.isEmpty(value)){
				throw new Exception("sheet["+sheet.getSheetName()+"].cell["+coordinate+"]的值为空");
			}
			return value;
		} catch (Exception e) {
			throw new Exception("无法获取sheet["+sheet.getSheetName()+"].cell["+coordinate+"]的值",e);
		}
	}
	static boolean isJSON(String s){
		return StringUtils.isNotEmpty(s) && s.startsWith("#{") && s.trim().endsWith("}");
	}
	static int getReportCellType(String value) throws Exception{
		if(isJSON(value)){
			ObjectMapper mapper=new ObjectMapper();
			mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES,true);
			ReportCell cell=mapper.readValue(value.substring(1), ReportCell.class);
			int celltype=cell.getType();
			return celltype;
		}
		return Const.REPORT_CELL_TYPE_SIMPLE;
	}
	static DataSetData excelRangeToData(Sheet sheet,FormulaEvaluator evaluator,String begin,String end) throws Exception{
		try {
			Coordinate b=new Coordinate(begin);
			Coordinate e=new Coordinate(end);
			int rb=(b.row<=e.row)?b.row:e.row;
			int re=(b.row<=e.row)?e.row:b.row;
			int cb=(b.col<=e.col)?b.col:e.col;
			int ce=(b.col<=e.col)?e.col:b.col;
			DataSetData data=new DataSetData();
			for(int c=cb;c<=ce;c++){
				Cell cell=sheet.getRow(rb).getCell(c);
				String field=getExcelCellFormattedValue(cell, evaluator);
				if(!data.addField(field)){
					throw new Exception("指定区域内的字段信息不能为空，且不能重复");
				}
			}
			if(data.getFieldCount()==0){
				throw new Exception("指定区域内缺少有效的字段信息");
			}
			for(int r=rb+1;r<=re;r++){
				Map<String,String> record=new HashMap<String, String>();
				for(int c=cb;c<=ce;c++){
					Cell cell=sheet.getRow(r).getCell(c);
					String value=getExcelCellFormattedValue(cell, evaluator);
					record.put(data.getField(c-cb), value);
				}
				data.addRecord(record);
			}
			return data;
		} catch (Exception e) {
			throw new Exception("无法将sheet["+sheet.getSheetName()+"].range["+begin+","+end+"]转换为数据集的静态数据",e);
		}
	}
	public static String getExcelCellFormattedValue(Cell cell,FormulaEvaluator evaluator){
		if(cell==null){
			return "";
		}
		DataFormatter formatter=new DataFormatter();
		String value=formatter.formatCellValue(cell, evaluator);
		if(value==null){
			value="";
		}
		return value;
	}
	static void initPageSettings(Sheet sheet,PageCell page,int rowcount,int colcount) throws Exception{
		float fontWidth=getFontWith(sheet.getWorkbook());
		page.setFontWidth(fontWidth);
		//js
		if(StringUtils.isNotEmpty(page.getJavascript())){
			String js=page.getJavascript();
			try {
				String value=getSpecifiedExcelCellRawValue(sheet, js);
				page.setJavascript(value);
			} catch (Exception e) {
				throw new Exception("无法解析sheet["+sheet.getSheetName()+"].page[##].javascript["+js+"]的值",e);
			}
		}
		//floatings
		Set<String> floatings=page.getFloatings();
		for(String floating:floatings){
			try {
				String value=getSpecifiedExcelCellRawValue(sheet, floating);
				FloatingCell fc=(FloatingCell)convertToReportCell(Const.REPORT_CELL_TYPE_FLOATING, value);
				fc.setFrom(floating.toUpperCase());
				page.getFloatingCells().add(fc);
			} catch (Exception e) {
				throw new Exception("无法解析sheet["+sheet.getSheetName()+"].page[##].floatings["+floating+"]的值",e);
			}
		}
		int mergedCount=sheet.getNumMergedRegions();
		//cols
		{
			List<ColCell> cols=new ArrayList<ColCell>(colcount); 
			Row row=sheet.getRow(rowcount);
			for(int c=0;c<colcount;c++){
				for(int i=0;i<mergedCount;i++){
					CellRangeAddress range=sheet.getMergedRegion(i);
					if(range.isInRange(rowcount, c)){
						throw new Exception("sheet["+sheet.getSheetName()+"].col["+NumberUtil.to26(c+1)+"]含有合并单元格");
					}
				}
				try {
					Cell cell=row.getCell(c);
					String value=getExcelCellRawValue(cell);
					if(value==null || StringUtils.isEmpty(value.trim())){
						value="#{}";
					}
					ColCell cc=(ColCell)convertToReportCell(Const.REPORT_CELL_TYPE_COL_HEADER,value);
					cc.setIndex(c);
					cc.setWidth(sheet.isColumnHidden(c)?0:sheet.getColumnWidth(c));
					cols.add(cc);
				} catch (Exception e) {
					throw new Exception("无法解析sheet["+sheet.getSheetName()+"].col["+NumberUtil.to26(c+1)+"]的值，缺少必要的属性或不是有效的#JSON格式",e);
				}
			}
			page.getCols().addAll(cols);
		}
		//rows
		{
			List<RowCell> rows=new ArrayList<RowCell>(rowcount);
			for(int r=0;r<rowcount;r++){
				for(int i=0;i<mergedCount;i++){
					CellRangeAddress range=sheet.getMergedRegion(i);
					if(range.isInRange(r, colcount)){
						throw new Exception("sheet["+sheet.getSheetName()+"].row["+(r+1)+"]含有合并单元格");
					}
				}
				try {
					Row row=sheet.getRow(r);
					Cell cell=row.getCell(colcount);
					String value=getExcelCellRawValue(cell);
					if(value==null || StringUtils.isEmpty(value.trim())){
						value="#{}";
					}
					RowCell rc=(RowCell)convertToReportCell(Const.REPORT_CELL_TYPE_ROW_HEADER,value);
					rc.setIndex(r);
					rc.setHeight(row.getZeroHeight()?0:row.getHeight());
					rows.add(rc);
				} catch (Exception e) {
					throw new Exception("无法解析sheet["+sheet.getSheetName()+"].row["+(r+1)+"]的值，缺少必要的属性或不是有效的#JSON格式",e);
				}
			}
			page.getRows().addAll(rows);
		}
	}
	static float getFontWith(Workbook excel) {
		try {
			return SheetUtil2.getDefaultCharWidth(excel);
		} catch (Throwable e) {
			//缺字体
			return 8.0f;// * 96f / 72f; 
		}
	}
	static void fillReportCell(List<VisibleCell> cells,VisibleCell cell,int row,int col,Map<CellRangeAddress,Boolean> cras){
		if(cell==null){
			return;
		}
		if(cells.indexOf(cell)>=0){
			return;
		}
		cell.setRfrom(row);
		cell.setCfrom(col);
		cell.setTop(row);
		cell.setLeft(col);
		Set<CellRangeAddress> keys=cras.keySet();
		for(CellRangeAddress cra:keys){
			boolean b=cras.get(cra);
			if(cra.isInRange(row, col)){
				if(b) return;
				int rbegin=cra.getFirstRow();
				int rend=cra.getLastRow();
				int cbegin=cra.getFirstColumn();
				int cend=cra.getLastColumn();
				cell.setRbegin(rbegin);
				cell.setRend(rend);
				cell.setCbegin(cbegin);
				cell.setCend(cend);
				//lazy
				int index=cells.size();
				for(int len=cells.size(),i=len-1;i>=0;i--){
					int lazy=cells.get(i).getLazy();
					if(lazy>cell.getLazy()){
						index--;
						break;
					}
				}
				cells.add(index,cell);
				cras.put(cra, true);
				return;
			}
		}
		int rbegin=row;
		int rend=row;
		int cbegin=col;
		int cend=col;
		cell.setRbegin(rbegin);
		cell.setRend(rend);
		cell.setCbegin(cbegin);
		cell.setCend(cend);
		//lazy
		int index=cells.size();
		for(int len=cells.size(),i=len-1;i>=0;i--){
			int lazy=cells.get(i).getLazy();
			if(lazy>cell.getLazy()){
				index--;
				break;
			}
		}
		cells.add(index,cell);
	}
	static String excelSheetToHTML(Sheet sheet) throws Exception{
		Set<Cell> cells=new HashSet<Cell>();
		Map<CellRangeAddress,Boolean> cras=new HashMap<CellRangeAddress,Boolean>();
		try {
			int mercount=sheet.getNumMergedRegions();
			for(int i=0;i<mercount;i++){
				CellRangeAddress ca=sheet.getMergedRegion(i);
				cras.put(ca, false);
			}
			String code=sheet.getSheetName();
			Coordinate coordinate=getTrueRowColCount(sheet);
			final int rowCount=coordinate.row;
			final int colCount=coordinate.col;
			FormulaEvaluator evaluator=sheet.getWorkbook().getCreationHelper().createFormulaEvaluator();
			float fontWidth=getFontWith(sheet.getWorkbook());
			//css
			String css="";
			{
				Map<String,String> styles=new HashMap<String, String>();
				for(int r=0;r<rowCount;r++){
					Row row=sheet.getRow(r);
					for(int c=0;c<colCount;c++){
						Cell cell=row.getCell(c);
						if(cell==null){continue;}
						String classname=".report-"+code+"-"+r+"-"+c;
						if(styles.containsKey(classname)){continue;}
						CellStyle topLeftStyle=getExcelCellStyle(sheet, r, c);//sheet.getRow(cell.getR()).getCell(cell.getC()).getCellStyle();
						CellStyle bottomRightStyle=getExcelCellRangeBottomRightStyle(sheet, r, c);
						if(topLeftStyle==null){continue;}
						StringBuilder sb=new StringBuilder();
						sb.append(CssUtil.background(topLeftStyle));
						sb.append(CssUtil.font(topLeftStyle,1.0f,sheet));
						sb.append(CssUtil.align(topLeftStyle));
						sb.append(CssUtil.border(topLeftStyle,bottomRightStyle,sheet));
						sb.append(CssUtil.wordBreak(topLeftStyle));
						//sb.append(CssUtil.transform(topLeftStyle));//TD转就乱了，应该是TD子元素转
						sb.append("overflow:hidden;");
						sb.append("text-overflow:ellipsis;");
						//sb.append("padding:0 0 0 1px;");
						styles.put(classname, sb.toString());
					}
				}
				StringBuilder sb=new StringBuilder();
				Set<String> classes=styles.keySet();
				sb.append("\n");
				sb.append("<style type=\"text/css\">");
				sb.append(".x-field-tail{display:none;}\n");
				for(String classname:classes){
					sb.append(classname).append("{").append(styles.get(classname)).append("}").append("\n");
				}
				sb.append("</style>");
				sb.append("\n");
				css=sb.toString();
			}
			//table
			String table="";
			{
				StringBuilder colgroup=new StringBuilder();
				colgroup.append("<colgroup>");
				int tableWidth=0;
				for(int col=0;col<colCount;col++){
					int width=getColPixelWidth(sheet,col,fontWidth,1.0f);
					tableWidth+=width;
					colgroup.append("<col style=").append("\"").append("width:").append(width).append("px;").append("\"").append(">").append("</col>");
				}
				colgroup.append("</colgroup>");
				StringBuilder sb=new StringBuilder();
				sb.append("\n");
				sb.append("<table").append(" id=").append("\"").append("report-").append(code).append("\"").append(" class=").append("\"report\"").append(" style=").append("\"").append("width:").append(tableWidth).append("px;").append("border-collapse:collapse;").append("table-layout:fixed;").append("\"").append(">");
				sb.append(colgroup.toString());
				for(int r=0;r<rowCount;r++){
					Row row=sheet.getRow(r);
					sb.append("<tr style=").append("\"").append("height:").append(getRowPixelHeight(sheet, r, 1.0f)).append("px;").append("\"").append(">");
					for(int c=0;c<colCount;c++){
						Cell cell=row.getCell(c);
						int rowspan=1;
						int colspan=1;
						Set<CellRangeAddress> ranges=cras.keySet();
						for(CellRangeAddress range:ranges){
							if(range.isInRange(r, c)){
								if(cras.get(range) && !cells.contains(cell)){
									cells.add(cell);
								} else {
									rowspan=range.getLastRow()-range.getFirstRow()+1;
									colspan=range.getLastColumn()-range.getFirstColumn()+1;
									cras.put(range, true);
								}
							}
						}
						if(cells.contains(cell)){
							continue;
						} else {
							cells.add(cell);
						}
						if(cell==null){
							sb.append("<td")
		    					.append(" rowspan=").append("\"").append(rowspan).append("\"")
		    					.append(" colspan=").append("\"").append(colspan).append("\"")
		    					.append(">");
							continue;
						}
						String cssClass="report-"+code+"-"+r+"-"+c;
						sb.append("<td")
							.append(" rowspan=").append("\"").append(rowspan).append("\"")
							.append(" colspan=").append("\"").append(colspan).append("\"")
							.append(" class=").append("\"").append(cssClass).append("\"")
							.append(" style=").append("\"").append("\"")
							.append(">");
						sb.append(getExcelCellFormattedValue(cell, evaluator));
						sb.append("</td>");
					}
					sb.append("</tr>");
				}
				sb.append("</table>");
				sb.append("\n");
				table=sb.toString();
			}
			return css+table;
		} catch (Exception e) {
			throw new Exception("无法将sheet["+sheet.getSheetName()+"]的内容转换为HTML",e);
		}
	}
	static CellStyle getExcelCellStyle(Sheet excelsheet,int row,int col){
		if(excelsheet==null){
			return null;
		}
		Row excelrow=excelsheet.getRow(row);
		if(excelrow==null){
			return null;
		}
		Cell excelcell=excelrow.getCell(col);
		if(excelcell==null){
			return null;
		}
		CellStyle style=excelcell.getCellStyle();
		return style;
	}
	static int getColPixelWidth(Sheet sheet,int col,float fontWidth,float zoom){
		if(sheet.isColumnHidden(col)){
			return 1;
		}
		float widthIn256 = sheet.getColumnWidth(col)*zoom;
		return (int)(widthIn256/256*fontWidth)+1;
	}
	static int getRowPixelHeight(Sheet sheet,int row,float zoom){
		if(sheet.getRow(row).getZeroHeight()){
			return 1;
		}
		float h=sheet.getRow(row).getHeight()*zoom/15f;
		return (int)h+1;// (h*96f/72f)+1;
	}
	static CellStyle getExcelCellRangeBottomRightStyle(Sheet sheet, int r, int c){
		int count=sheet.getNumMergedRegions();
		for(int i=0;i<count;i++){
			if(sheet.getMergedRegion(i).isInRange(r, c)){
				return getExcelCellStyle(sheet, sheet.getMergedRegion(i).getLastRow(), sheet.getMergedRegion(i).getLastColumn());
			}
		}
		return getExcelCellStyle(sheet, r, c);
	}
	static Block executeSimpleCell(SimpleCell cell) throws Exception{
		String value=cell.getValue();
		Block block=Block.SingleBlock(new NormalResultCell(cell,value));
		return block;
	}
	static Block executeFormCell(FormCell cell) throws Exception{
		Block block=Block.SingleBlock(new FormResultCell(cell));
		return block;
	}
	static Block executeStaticCell(StaticCell cell, DataSetData data) throws Exception{
		try {
			final int skip=cell.getSkip();
			final int count=data.size();
			final int field_count=data.getFieldCount();
			final int row_inc=cell.getRowInc();
			final int col_inc=cell.getColInc();
			final int rbegin=0;
			final int rend=row_inc-1;
			final int cbegin=0;
			final int cend=col_inc-1;
			final String str=cell.getValue();
			boolean merge=cell.isAutoMerge();
			Block block=Block.ExtendableBlock(cell, count, field_count);
			switch (cell.getExtend()) {
			case Const.CELL_EXTEND_NONE:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,str,null));
					break;
				}
				Map<String,String> record=data.getRecord(0);
				block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,str,record));
				break;
			}
			case Const.CELL_EXTEND_BOTTOM:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,str,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					int rb=rbegin;
					int re=rend;
					
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							re+=row_inc;
							
						} else {
							block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,str,priorRecord));
							prior=value;
							priorRecord=record;
							rb=rbegin+i*(row_inc+skip);
							re=rend+i*(row_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,str,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin,cend,cell,str,record));
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT_BOTTOM:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin,rend,cbegin+_*col_inc,cend+_*col_inc,cell,str,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int rb=rbegin;
						int re=rend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								re+=row_inc;
								
							} else {
								block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,str,priorRecord));
								prior=value;
								priorRecord=record;
								rb=rbegin+i*(row_inc+skip);
								re=rend+i*(row_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,str,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin+_*col_inc,cend+_*col_inc,cell,str,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_BOTTOM_RIGHT:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin,cend,cell,str,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int cb=cbegin;
						int ce=cend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								ce+=col_inc;
								
							} else {
								block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,str,priorRecord));
								prior=value;
								priorRecord=record;
								cb=cbegin+i*(col_inc+skip);
								ce=cend+i*(col_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,str,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,str,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,str,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					int cb=cbegin;
					int ce=cend;
					
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							ce+=col_inc;
							
						} else {
							block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,str,priorRecord));
							prior=value;
							priorRecord=record;
							cb=cbegin+i*(col_inc+skip);
							ce=cend+i*(col_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,str,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin,rend,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,str,record));
					}
				}
				break;
			}
			default:{
				break;
			}
			}
			return block;
		} finally {
			//
		}
	}
	static Block executeFieldCell(FieldCell cell,DataSetData data) throws Exception{
		try {
			final int skip=cell.getSkip();
			final int count=data.size();
			final int field_count=data.getFieldCount();
			final int row_inc=cell.getRowInc();
			final int col_inc=cell.getColInc();
			final int rbegin=0;
			final int rend=row_inc-1;
			final int cbegin=0;
			final int cend=col_inc-1;
			boolean merge=cell.isAutoMerge();
			Block block=Block.ExtendableBlock(cell, count, field_count);
			switch (cell.getExtend()) {
			case Const.CELL_EXTEND_NONE:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,null,null));
					break;
				}
				String field=cell.getField();
				String value=data.getValue(0, field);
				Map<String,String> record=data.getRecord(0);
				block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,value,record));
				break;
			}
			case Const.CELL_EXTEND_BOTTOM:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,null,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					int rb=rbegin;
					int re=rend;
					
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							re+=row_inc;
							
						} else {
							block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,prior,priorRecord));
							prior=value;
							priorRecord=record;
							rb=rbegin+i*(row_inc+skip);
							re=rend+i*(row_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,prior,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin,cend,cell,value,record));
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT_BOTTOM:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin,rend,cbegin+_*col_inc,cend+_*col_inc,cell,null,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int rb=rbegin;
						int re=rend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								re+=row_inc;
								
							} else {
								block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,prior,priorRecord));
								prior=value;
								priorRecord=record;
								rb=rbegin+i*(row_inc+skip);
								re=rend+i*(row_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,prior,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin+_*col_inc,cend+_*col_inc,cell,value,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_BOTTOM_RIGHT:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin,cend,cell,null,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int cb=cbegin;
						int ce=cend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								ce+=col_inc;
								
							} else {
								block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,prior,priorRecord));
								prior=value;
								priorRecord=record;
								cb=cbegin+i*(col_inc+skip);
								ce=cend+i*(col_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,prior,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,value,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,null,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					int cb=cbegin;
					int ce=cend;
					
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							ce+=col_inc;
							
						} else {
							block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,prior,priorRecord));
							prior=value;
							priorRecord=record;
							cb=cbegin+i*(col_inc+skip);
							ce=cend+i*(col_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,prior,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin,rend,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,value,record));
					}
				}
				break;
			}
			default:{
				break;
			}
			}
			return block;
		} finally {
			//
		}
	}
	static Block executeDataCell(DataCell cell,DataSetData data) throws Exception{
		try {
			final int skip=cell.getSkip();
			final int count=data.size();
			final int field_count=data.getFieldCount();
			final int row_inc=cell.getRowInc();
			final int col_inc=cell.getColInc();
			final int rbegin=0;
			final int rend=row_inc-1;
			final int cbegin=0;
			final int cend=col_inc-1;
			boolean merge=cell.isAutoMerge();
			Block block=Block.ExtendableBlock(cell, count, field_count);
			switch (cell.getExtend()) {
			case Const.CELL_EXTEND_NONE:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,null,null));
					break;
				}
				String field=cell.getField();
				String value=data.getValue(0, field);
				Map<String,String> record=data.getRecord(0);
				block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,value,record));
				break;
			}
			case Const.CELL_EXTEND_BOTTOM:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,null,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					int rb=rbegin;
					int re=rend;
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							re+=row_inc;
						} else {
							block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,prior,priorRecord));
							prior=value;
							priorRecord=record;
							rb=rbegin+i*(row_inc+skip);
							re=rend+i*(row_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,prior,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin,cend,cell,value,record));
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT_BOTTOM:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin,rend,cbegin+_*col_inc,cend+_*col_inc,cell,null,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int rb=rbegin;
						int re=rend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								re+=row_inc;
								
							} else {
								block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,prior,priorRecord));
								prior=value;
								priorRecord=record;
								rb=rbegin+i*(row_inc+skip);
								re=rend+i*(row_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,prior,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin+_*col_inc,cend+_*col_inc,cell,value,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_BOTTOM_RIGHT:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin,cend,cell,null,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int cb=cbegin;
						int ce=cend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								ce+=col_inc;
								
							} else {
								block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,prior,priorRecord));
								prior=value;
								priorRecord=record;
								cb=cbegin+i*(col_inc+skip);
								ce=cend+i*(col_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,prior,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,value,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,null,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					int cb=cbegin;
					int ce=cend;
					
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							ce+=col_inc;
							
						} else {
							block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,prior,priorRecord));
							prior=value;
							priorRecord=record;
							cb=cbegin+i*(col_inc+skip);
							ce=cend+i*(col_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,prior,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin,rend,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,value,record));
					}
				}
				break;
			}
			default:{
				break;
			}
			}
			return block;
		} finally {
			//
		}
	}
	static Block executeExpressionCell(ExpressionCell cell, DataSetData data) throws Exception{
		try {
			final int skip=cell.getSkip();
			final int count=data.size();
			final int field_count=data.getFieldCount();
			final int row_inc=cell.getRowInc();
			final int col_inc=cell.getColInc();
			final int rbegin=0;
			final int rend=row_inc-1;
			final int cbegin=0;
			final int cend=col_inc-1;
			final String exp=cell.getExp();
			boolean merge=cell.isAutoMerge();
			Block block=Block.ExtendableBlock(cell, count, field_count);
			switch (cell.getExtend()) {
			case Const.CELL_EXTEND_NONE:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,exp,null));
					break;
				}
				Map<String,String> record=data.getRecord(0);
				block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,exp,record));
				break;
			}
			case Const.CELL_EXTEND_BOTTOM:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,exp,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					int rb=rbegin;
					int re=rend;
					
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							re+=row_inc;
							
						} else {
							block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,exp,priorRecord));
							prior=value;
							priorRecord=record;
							rb=rbegin+i*(row_inc+skip);
							re=rend+i*(row_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rb,re,cbegin,cend,cell,exp,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin,cend,cell,exp,record));
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT_BOTTOM:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin,rend,cbegin+_*col_inc,cend+_*col_inc,cell,exp,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int rb=rbegin;
						int re=rend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								re+=row_inc;
								
							} else {
								block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,exp,priorRecord));
								prior=value;
								priorRecord=record;
								rb=rbegin+i*(row_inc+skip);
								re=rend+i*(row_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,exp,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin+_*col_inc,cend+_*col_inc,cell,exp,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_BOTTOM_RIGHT:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin,cend,cell,exp,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int cb=cbegin;
						int ce=cend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								ce+=col_inc;
								
							} else {
								block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,exp,priorRecord));
								prior=value;
								priorRecord=record;
								cb=cbegin+i*(col_inc+skip);
								ce=cend+i*(col_inc+skip);
							}
						}
						block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,exp,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							Map<String,String> record=data.getRecord(i);
							block.fill(new NormalResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,exp,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT:{
				if(count==0){
					block.fill(new NormalResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,exp,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					int cb=cbegin;
					int ce=cend;
					
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							ce+=col_inc;
							
						} else {
							block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,exp,priorRecord));
							prior=value;
							priorRecord=record;
							cb=cbegin+i*(col_inc+skip);
							ce=cend+i*(col_inc+skip);
						}
					}
					block.fill(new NormalResultCell(rbegin,rend,cb,ce,cell,exp,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						Map<String,String> record=data.getRecord(i);
						block.fill(new NormalResultCell(rbegin,rend,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,exp,record));
					}
				}
				break;
			}
			default:{
				break;
			}
			}
			return block;
		} finally {
			//
		}
	}
	static Block executeChartCell(ChartCell cell, DataSetData data) throws Exception{
		try {
			final int skip=cell.getSkip();
			final int count=data.size();
			final int field_count=data.getFieldCount();
			final int row_inc=cell.getRowInc();
			final int col_inc=cell.getColInc();
			final int rbegin=0;
			final int rend=row_inc-1;
			final int cbegin=0;
			final int cend=col_inc-1;
			final Series series=cell.getSeries();
			boolean merge=cell.isAutoMerge();
			Block block=Block.ExtendableBlock(cell, count, field_count);
			switch (cell.getExtend()) {
			case Const.CELL_EXTEND_NONE:{
				if(count==0){
					block.fill(new ChartResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,series,null));
					break;
				}
				Map<String,String> record=data.getRecord(0);
				block.fill(new ChartResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,series,record));
				break;
			}
			case Const.CELL_EXTEND_BOTTOM:{
				if(count==0){
					block.fill(new ChartResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,series,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					int rb=rbegin;
					int re=rend;
					
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							re+=row_inc;
							
						} else {
							block.fill(new ChartResultCell(rb,re,cbegin,cend,cell,series,priorRecord));
							prior=value;
							priorRecord=record;
							rb=rbegin+i*(row_inc+skip);
							re=rend+i*(row_inc+skip);
						}
					}
					block.fill(new ChartResultCell(rb,re,cbegin,cend,cell,series,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						Map<String,String> record=data.getRecord(i);
						block.fill(new ChartResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin,cend,cell,series,record));
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT_BOTTOM:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new ChartResultCell(rbegin,rend,cbegin+_*col_inc,cend+_*col_inc,cell,series,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int rb=rbegin;
						int re=rend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								re+=row_inc;
								
							} else {
								block.fill(new ChartResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,series,priorRecord));
								prior=value;
								priorRecord=record;
								rb=rbegin+i*(row_inc+skip);
								re=rend+i*(row_inc+skip);
							}
						}
						block.fill(new ChartResultCell(rb,re,cbegin+_*col_inc,cend+_*col_inc,cell,series,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							Map<String,String> record=data.getRecord(i);
							block.fill(new ChartResultCell(rbegin+i*(row_inc+skip),rend+i*(row_inc+skip),cbegin+_*col_inc,cend+_*col_inc,cell,series,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_BOTTOM_RIGHT:{
				if(count==0){
					for(int _=0;_<field_count;_++){
						block.fill(new ChartResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin,cend,cell,series,null));
					}
					break;
				}
				for(int _=0;_<field_count;_++){
					String field=data.getField(_);
					if(merge && count>1){
						int cb=cbegin;
						int ce=cend;
						
						String prior=data.getValue(0,field);
						Map<String,String> priorRecord=data.getRecord(0);
						for(int i=1;i<count;i++){
							String value=data.getValue(i,field);
							Map<String,String> record=data.getRecord(i);
							if((prior!=null) && (prior.equals(value))){
								ce+=col_inc;
								
							} else {
								block.fill(new ChartResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,series,priorRecord));
								prior=value;
								priorRecord=record;
								cb=cbegin+i*(col_inc+skip);
								ce=cend+i*(col_inc+skip);
							}
						}
						block.fill(new ChartResultCell(rbegin+_*row_inc,rend+_*row_inc,cb,ce,cell,series,priorRecord));
					} else {
						for(int i=0;i<count;i++){
							Map<String,String> record=data.getRecord(i);
							block.fill(new ChartResultCell(rbegin+_*row_inc,rend+_*row_inc,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,series,record));
						}
					}
				}
				break;
			}
			case Const.CELL_EXTEND_RIGHT:{
				if(count==0){
					block.fill(new ChartResultCell(0,cell.getRend()-cell.getRbegin(),0,cell.getCend()-cell.getCbegin(),cell,series,null));
					break;
				}
				String field=cell.getField();
				if(merge && count>1){
					int cb=cbegin;
					int ce=cend;
					
					String prior=data.getValue(0,field);
					Map<String,String> priorRecord=data.getRecord(0);
					for(int i=1;i<count;i++){
						String value=data.getValue(i,field);
						Map<String,String> record=data.getRecord(i);
						if((prior!=null) && (prior.equals(value))){
							ce+=col_inc;
							
						} else {
							block.fill(new ChartResultCell(rbegin,rend,cb,ce,cell,series,priorRecord));
							prior=value;
							priorRecord=record;
							cb=cbegin+i*(col_inc+skip);
							ce=cend+i*(col_inc+skip);
						}
					}
					block.fill(new ChartResultCell(rbegin,rend,cb,ce,cell,series,priorRecord));
				} else {
					for(int i=0;i<count;i++){
						Map<String,String> record=data.getRecord(i);
						block.fill(new ChartResultCell(rbegin,rend,cbegin+i*(col_inc+skip),cend+i*(col_inc+skip),cell,series,record));
					}
				}
				break;
			}
			default:{
				break;
			}
			}
			return block;
		} finally {
			//
		}
	}
	/*
	static XSSFWorkbook toExcel(byte[] middle) throws Exception{
		ByteArrayInputStream in=new ByteArrayInputStream(middle);
		try {
			XSSFWorkbook excel=new XSSFWorkbook(in);
			return excel;
		} finally {
			in.close();
		}
	}
	*/
	static void setExcelCellValue(Cell cell,ResultCell rc,Map<String, Serializable> params,Map<String, String> consts,Map<String,DataSetData> datas) throws Exception{
		if(rc==null){
			cell.setCellType(CellType.BLANK);
			return;
		}
		String value=rc.getValue();
		if(value==null){
			cell.setCellType(CellType.BLANK);
			return;
		}
		switch (rc.getDataType()) {
		case Const.CELL_DATA_TYPE_STRING:{
			cell.setCellValue(value);
			break;
		}
		case Const.CELL_DATA_TYPE_INT:{
			try {
				cell.setCellValue(Integer.valueOf(value));
			} catch (Exception e) {
				cell.setCellValue(value);
			}
			break;
		}
		case Const.CELL_DATA_TYPE_DOUBLE:{
			try {
				cell.setCellValue(Double.valueOf(value));
			} catch (Exception e) {
				cell.setCellValue(value);
			}
			break;
		}
		case Const.CELL_DATA_TYPE_DATE:{
			try {
				SimpleDateFormat fromat = new SimpleDateFormat("yyyy-MM-dd");
				cell.setCellValue(fromat.parse(value));
			} catch (Exception e) {
				cell.setCellValue(value);
			}
			break;
		}
		case Const.CELL_DATA_TYPE_EXP:{
			Map<String,Object> kv=new HashMap<String, Object>();
			kv.putAll(datas);
			kv.putAll(params);
			kv.put("CONSTS",consts);
			kv.put("COL",rc.getCbegin()+1);
			kv.put("ROW", rc.getRbegin()+1);
			kv.put("DATA", rc.getRecord());
			cell.setCellType(CellType.FORMULA);
			try {
				value=RenderUtil.renderString(value, kv);
			} catch (Exception e) {
				throw new Exception("无法预处理sheet["+cell.getSheet().getSheetName()+"].cell["+NumberUtil.toExcelPoint(rc.getRfrom(), rc.getCfrom())+"]的公式："+value,e);
			}
			cell.setCellFormula(value);
			break;
		}
		case Const.CELL_DATA_TYPE_DYNAMIC_STRING:{
			Map<String,Object> kv=new HashMap<String, Object>();
			kv.putAll(datas);
			kv.putAll(params);
			kv.put("CONSTS",consts);
			kv.put("COL",rc.getCbegin()+1);
			kv.put("ROW", rc.getRbegin()+1);
			kv.put("DATA", rc.getRecord());
			try {
				value=RenderUtil.renderString(value, kv);
			} catch (Exception e) {
				throw new Exception("无法预处理sheet["+cell.getSheet().getSheetName()+"].cell["+NumberUtil.toExcelPoint(rc.getRfrom(), rc.getCfrom())+"]的动态字符串："+value,e);
			}
			cell.setCellValue(value);
			break;
		}
		default:
			cell.setCellValue(value);
			break;
		}
	}
	static CellStyle getDynamicExcelCellStyle(Workbook excel,FormulaEvaluator evaluator,Sheet sheet,Cell cell,ResultCell rc,Map<String,Serializable> params,Map<String,String> consts,Map<String,DataSetData> datas) throws Exception{
		String style=rc.getStyle();
		if(style==null || StringUtils.isEmpty(style.trim())){
			return getExcelCellStyle(sheet, rc.getRfrom(), rc.getCfrom());
		}
		Map<String,Object> vars=new HashMap<String, Object>();
		vars.putAll(datas);
		vars.putAll(params);
		vars.put("CONSTS", consts);
		vars.put("COL",rc.getCbegin()+1);
		vars.put("ROW", rc.getRbegin()+1);
		vars.put("DATA", rc.getRecord());
		vars.put("VALUE", getExcelCellResultValue(cell, evaluator));
		try {
			style=RenderUtil.renderString(style, vars);
			if(StringUtils.isEmpty(style)){
				return getExcelCellStyle(sheet, rc.getRfrom(), rc.getCfrom());
			}
			Coordinate coordinate=new Coordinate(style);
			CellStyle excelStyle=getSpecifiedExcelCellStyle(sheet, coordinate);
			rc.setStyleFrom(coordinate.row, coordinate.col);
			return excelStyle;
		} catch (Exception e) {
			throw new Exception("无法计算sheet["+sheet.getSheetName()+"].cell["+NumberUtil.toExcelPoint(rc.getRfrom(),rc.getCfrom())+"].style的值",e);
		}
	}
	static Serializable getExcelCellResultValue(Cell cell,FormulaEvaluator evaluator){
		if(cell==null){
			return null;
		}
		CellType type=cell.getCellTypeEnum();
		Serializable value=null;
		switch (type) {
		case BLANK:
			break;
		case BOOLEAN:
			value=cell.getBooleanCellValue();
			break;
		case ERROR:
			if(cell instanceof XSSFCell){
				value=((XSSFCell)cell).getErrorCellString();
			}else if(cell instanceof HSSFCell){
				value=cell.getErrorCellValue()+"";
			}
			break;
		case FORMULA:{
			CellValue cv=evaluator.evaluate(cell);
			switch (cv.getCellTypeEnum()) {
				case BLANK:
					break;
				case BOOLEAN:
					value=cv.getBooleanValue();
					break;
				case ERROR:
					value=cv.formatAsString();
					break;
				case NUMERIC:
					value=cv.getNumberValue();
					break;
				case STRING:
					value=cv.getStringValue();
					break;
				default:
					value=cv.formatAsString();
					break;
			}
			break;
		}
		case NUMERIC:
			value=cell.getNumericCellValue();
			break;
		case STRING:
			value=cell.getStringCellValue();
			break;
		default:
			break;
		}
		return value;
	}
	
	static void setExcelCellResultValue(Cell newCell,Cell cell,FormulaEvaluator evaluator){
		if(cell==null){
			return ;
		}
		CellType type=cell.getCellTypeEnum();
		switch (type) {
		case BLANK:
			break;
		case BOOLEAN:
			newCell.setCellValue(cell.getBooleanCellValue());
			break;
		case ERROR:
			if(cell instanceof XSSFCell && newCell instanceof XSSFCell){
				newCell.setCellValue(((XSSFCell)cell).getErrorCellString());
			}else if(cell instanceof HSSFCell && newCell instanceof HSSFCell){
				newCell.setCellValue(cell.getErrorCellValue()+"");
			}
			break;
		case FORMULA:{
			CellValue cv=evaluator.evaluate(cell);
			switch (cv.getCellTypeEnum()) {
				case BLANK:
					break;
				case BOOLEAN:
					newCell.setCellValue(cv.getBooleanValue());
					break;
				case ERROR:
					newCell.setCellValue(cv.formatAsString());
					break;
				case NUMERIC:
					newCell.setCellValue(cv.getNumberValue());
					break;
				case STRING:
					newCell.setCellValue(cv.getStringValue());
					break;
				default:
					newCell.setCellValue(cv.formatAsString());
					break;
			}
			break;
		}
		case NUMERIC:
			newCell.setCellValue(cell.getNumericCellValue());
			break;
		case STRING:
			newCell.setCellValue(cell.getStringCellValue());
			break;
		default:
			break;
		}
	}
	
	static CellStyle getSpecifiedExcelCellStyle(Sheet sheet,Coordinate coordinate) throws Exception{
		try {
			//Coordinate c=new Coordinate(coordinate);
			return getExcelCellStyle(sheet, coordinate.row, coordinate.col);
		} catch (Exception e) {
			throw new Exception("无法获取sheet["+sheet.getSheetName()+"].cell["+coordinate+"]的样式",e);
		}
	}
	static byte[] excelToBytes(Workbook excel) throws Exception{
		ByteArrayOutputStream out=new ByteArrayOutputStream();
		try {
			excel.write(out);
			out.flush();
			return out.toByteArray();
		} finally {
			out.close();
		}
	}
	static int getPixelWidth(ResultCell rc,Sheet sheet,float fontWidth,float zoom){
		int width=0;
		for(int i=rc.getCbegin();i<=rc.getCend();i++){
			width+=getColPixelWidth(sheet,i,fontWidth,zoom);
		}
		return width;
	}
	static int getPixelHeight(ResultCell rc,Sheet sheet,float zoom){
		int height=0;
		for(int i=rc.getRbegin();i<=rc.getRend();i++){
			height+=getRowPixelHeight(sheet, i, zoom);
		}
		return height;
	}
	static StringBuilder generateReportPreviewAttrs(PageCell page,Report report,Map<String,Integer> paging) throws Exception{
		StringBuilder sb=new StringBuilder();
		if(!page.isDynamicAttr()){
			return sb;
		}
		Map<String,String> attr=page.getAttr();
		if(attr.isEmpty()){
			return sb;
		}
		Map<String,Object> vars=new HashMap<String, Object>();
		vars.putAll(report.getDatas());
		vars.putAll(report.getParams());
		vars.put("CONSTS", report.getConsts());
		Set<String> keys=attr.keySet();
		for(String k:keys){
			if(k.equalsIgnoreCase("id") || k.equalsIgnoreCase("class") || k.equalsIgnoreCase("style") || k.equalsIgnoreCase("rowcount") || k.equalsIgnoreCase("colcount") || k.equalsIgnoreCase("titlecount") || k.equalsIgnoreCase("headercount") || k.equalsIgnoreCase("detailcount") || k.equalsIgnoreCase("footercount")){
				continue;
			}
			String v;
			try {
				v = RenderUtil.renderString(attr.get(k), vars);
			} catch (Exception e) {
				throw new Exception("sheet["+page.getFrom()+"].page.attr["+k+"]的值计算错误",e);
			}
			sb.append(" ").append(k).append("=").append("\"").append(v).append("\"");
			if(k.equalsIgnoreCase("page-size")){
				paging.put(k, Integer.valueOf(v));
			}
		}
		return sb;
	}
	static StringBuilder generateCellPreviewAttrs(PageCell page,Report report,ResultCell cell,Cell excelCell,FormulaEvaluator evaluator) throws Exception{
		StringBuilder sb=new StringBuilder();
		if(!page.isDynamicAttr()){
			return sb;
		}
		Map<String,String> attr=cell.getAttr();
		if(attr.isEmpty()){
			return sb;
		}
		Serializable value=getExcelCellResultValue(excelCell, evaluator);
		String display=(cell.getDataType()==Const.CELL_DATA_TYPE_DATE)?getExcelCellFormattedValue(excelCell,evaluator).replaceAll("\"", ""):getExcelCellFormattedValue(excelCell,evaluator);
		Map<String,Object> vars=new HashMap<String, Object>();
		vars.putAll(report.getDatas());
		vars.putAll(report.getParams());
		vars.put("CONSTS", report.getConsts());
		vars.put("ROW", cell.getRbegin()+1);
		vars.put("COL", cell.getCbegin()+1);
		vars.put("DATA", cell.getRecord());
		vars.put("VALUE", value);
		vars.put("DISPLAY", display);
		Set<String> keys=attr.keySet();
		for(String k:keys){
			if(k.equalsIgnoreCase("row") || k.equalsIgnoreCase("col") || k.equalsIgnoreCase("class") || k.equalsIgnoreCase("rowspan") || k.equalsIgnoreCase("colspan")){
				continue;
			}
			String v;
			try {
				v = RenderUtil.renderString(attr.get(k), vars);
			} catch (Exception e) {
				throw new Exception("sheet["+page.getFrom()+"].cell["+NumberUtil.toExcelPoint(cell.getRfrom(), cell.getCfrom())+"].attr["+k+"]的值计算错误",e);
			}
			sb.append(" ").append(k).append("=").append("\"").append(v).append("\"");
		}
		return sb;
	}
	/*
	static byte[] copy(byte[] source){
		byte[] data=new byte[source.length];
		System.arraycopy(source, 0, data, 0, source.length);
		return data;
	}
	*/
	//TODO:为编辑存根预留
	static String getExcelCellEditorValue(XSSFCell cell){
		if(cell==null){
			return "";
		}
		CellType type=cell.getCellTypeEnum();
		String value=null;
		switch (type) {
		case BLANK:
			break;
		case BOOLEAN:
			value=Boolean.toString(cell.getBooleanCellValue());
			break;
		case ERROR:
			value=cell.getErrorCellString();
			break;
		case FORMULA:
			value=cell.getCellFormula();
			break;
		case NUMERIC:
			value=Double.toString(cell.getNumericCellValue());
			break;
		case STRING:
			value=cell.getStringCellValue();
			break;
		default:
			break;
		}
		return value==null?"":value.replaceAll("\"", "&quot;").replaceAll("\n", "\\\\n");
	}
	static StringBuilder generatePreviewCss(PageCell page,Report report,Sheet sheet,float zoom){
		String code=report.getParams().get("SYS_REPORT_CODE").toString();
		Map<String,String> styles=new HashMap<String, String>();
		int rowCount=report.getRowCount(page.getName());
		int colCount=report.getColCount(page.getName());
		for(int row=0;row<rowCount;row++){
			for(int col=0;col<colCount;col++){
				ResultCell cell=report.getCell(page.getName(),row, col);
				if(cell==null){continue;}
				String classname=".report-"+code+"-"+cell.getStyleRfrom()+"-"+cell.getStyleCfrom();
				if(styles.containsKey(classname)){continue;}
				CellStyle topLeftStyle=getExcelCellStyle(sheet, cell.getStyleRfrom(), cell.getStyleCfrom());//sheet.getRow(cell.getR()).getCell(cell.getC()).getCellStyle();
				CellStyle bottomRightStyle=getExcelCellRangeBottomRightStyle(sheet, cell.getStyleRfrom(), cell.getStyleCfrom());
				if(topLeftStyle==null){continue;}
				StringBuilder css=new StringBuilder();
				css.append(CssUtil.background(topLeftStyle));
				css.append(CssUtil.font(topLeftStyle,zoom,sheet));
				css.append(CssUtil.align(topLeftStyle));
				css.append(CssUtil.border(topLeftStyle,bottomRightStyle,sheet));
				css.append(CssUtil.wordBreak(topLeftStyle));
				//css.append(CssUtil.transform(topLeftStyle));//TD转就乱了，应该是TD子元素转
				css.append("overflow:hidden;");
				css.append("text-overflow:ellipsis;");
				//css.append("padding:0 0 0 1px;");
				styles.put(classname, css.toString());
			}
		}
		StringBuilder sb=new StringBuilder();
		Set<String> classes=styles.keySet();
		sb.append("\n");
		sb.append("<style type=\"text/css\">");
		sb.append(".x-field-tail{display:none;}\n");
		for(String classname:classes){
			sb.append(classname).append("{").append(styles.get(classname)).append("}").append("\n");
		}
		sb.append("</style>");
		sb.append("\n");
		return sb;
	}
	static StringBuilder generatePreviewJs(PageCell page,Report report){
		StringBuilder sb=new StringBuilder();
		sb.append("\n");
		sb.append("<script type=\"text/javascript\">");
		sb.append("\n///\n");
		sb.append(page.getJavascript());
		sb.append("\n///\n");
		sb.append("</script>");
		sb.append("\n");
		return sb;
	}
	static String generateCellPreviewContent(PageCell page,Report report,ResultCell cell,Cell excelCell,FormulaEvaluator evaluator,float zoom) throws Exception{
		//TODO: 还有电子签章，即PageCell中的floating
		//这里不要用DataType来判断，因为电子签章是floating的并且DataType和ChartResultCell是一样，都是IMG，floating需要z-index以及计算绝对坐标
		if(cell instanceof ChartResultCell){
			StringBuilder sb=new StringBuilder();
			byte[] png=ImageUtil.zoom(report.getImages(page.getName()).get(cell),zoom);
			String data=Base64.encodeBase64String(png);
			sb.append("<img")
				//.append(" style=").append("\"").append("display:block;width:100%;height:100%;").append("\"")
				.append(" style=").append("\"").append("display:block;").append("\"")//IE8 height width
				.append(" alt=").append("\"").append("chart").append("\"")
				.append(" src=").append("\"").append("data:image/png;base64,").append(data).append("\"")
				.append("/>");
			return sb.toString();
		}
		if(cell instanceof FormResultCell){
			FormCell form=renderFormCell(((FormResultCell) cell).getForm(),report);
			Map<String,Object> values=new HashMap<String, Object>();
			values.put("CONSTS",report.getConsts());
			values.put("params",report.getParams());
			values.put("item", form);
			if(form instanceof DataSetListFormCell){
				String dataset=((DataSetListFormCell)form).getDataset();
				DataSetData data=report.getDatas().get(dataset);
				if(data==null){
					throw new Exception("表单["+form.getCode()+"]对应的数据集["+dataset+"]不存在");
				}
				List<Map<String,String>> raw=DataSetData.toFormList(data);
				ObjectMapper mapper=new ObjectMapper();
				String json=mapper.writeValueAsString(raw);
				values.put("data", json);
			}
			if(form instanceof DataSetTreeFormCell){
				String dataset=((DataSetTreeFormCell)form).getDataset();
				DataSetData data=report.getDatas().get(dataset);
				if(data==null){
					throw new Exception("表单["+form.getCode()+"]对应的数据集["+dataset+"]不存在");
				}
				List<TreeNode> raw=DataSetData.toFormTree(data);
				ObjectMapper mapper=new ObjectMapper();
				String json=mapper.writeValueAsString(raw);
				values.put("data", json);
			}
			if(form instanceof UrlListFormCell){
				String url=((UrlListFormCell)form).getUrl();
				Map<String,Object> vars=new HashMap<String, Object>();
				vars.putAll(report.getDatas());
				vars.putAll(report.getParams());
				vars.put("CONSTS", report.getConsts());
				vars.put("_", form.getCode());
				url=RenderUtil.renderString(url, vars);
				/*
				if(url.startsWith("/")){
					String _=url.substring(1);
					ComponentURLConverter cuc=ComponentURLConverter.getInstance();
					url=cuc.converteURL(_);
				}
				*/
				values.put("url", StringEscapeUtils.escapeEcmaScript(url));
			}
			if(form instanceof UrlTreeFormCell){
				String url=((UrlTreeFormCell)form).getUrl();
				Map<String,Object> vars=new HashMap<String, Object>();
				vars.putAll(report.getDatas());
				vars.putAll(report.getParams());
				vars.put("CONSTS", report.getConsts());
				vars.put("_", form.getCode());
				url=RenderUtil.renderString(url, vars);
				/*
				if(url.startsWith("/")){
					String _=url.substring(1);
					ComponentURLConverter cuc=ComponentURLConverter.getInstance();
					url=cuc.converteURL(_);
				}
				*/
				values.put("url", StringEscapeUtils.escapeEcmaScript(url));
			}
			if(form instanceof UrlTreeSelectFormCell){
				String url=((UrlTreeSelectFormCell)form).getUrl();
				String sofaUrl=((UrlTreeSelectFormCell)form).getSofaUrl();
				Map<String,Object> vars=new HashMap<String, Object>();
				vars.putAll(report.getDatas());
				vars.putAll(report.getParams());
				vars.put("CONSTS", report.getConsts());
				vars.put("_", form.getCode());
				url=RenderUtil.renderString(url, vars);
				sofaUrl=RenderUtil.renderString(sofaUrl, vars);
				/*
				if(url.startsWith("/")){
					String _=url.substring(1);
					ComponentURLConverter cuc=ComponentURLConverter.getInstance();
					url=cuc.converteURL(_);
				}
				*/
				values.put("url", StringEscapeUtils.escapeEcmaScript(url));
				values.put("sofaUrl", StringEscapeUtils.escapeEcmaScript(sofaUrl));
			}
			String path=form.isCustom()?Activator.CUSTOM_FORM_PATH:Activator.FORM_PATH;
			return RenderUtil.renderFile(path,form.getRenderSource(), values);
		}
		String display=(cell.getDataType()==Const.CELL_DATA_TYPE_DATE)?getExcelCellFormattedValue(excelCell,evaluator).replaceAll("\"", ""):getExcelCellFormattedValue(excelCell,evaluator);
		if(page.isDynamicHTML()){
			String html=cell.getHtml();
			if(StringUtils.isNotEmpty(html)){
				Serializable value=getExcelCellResultValue(excelCell, evaluator);
				Map<String,Object> vars=new HashMap<String, Object>();
				vars.putAll(report.getDatas());
				vars.putAll(report.getParams());
				vars.put("CONSTS", report.getConsts());
				vars.put("ROW", cell.getRbegin()+1);
				vars.put("COL", cell.getCbegin()+1);
				vars.put("DATA", cell.getRecord());
				vars.put("VALUE", value);
				vars.put("DISPLAY", display);
				return RenderUtil.renderString(html, vars);
			} else if(cell.isEscape(page)){
				return display.replaceAll("&", "&amp;")
						  .replaceAll("<", "&lt;")
						  .replaceAll(">", "&gt;")
						  .replaceAll("\"", "&quot;")
						  .replaceAll(" ", "&nbsp;")
						  .replaceAll("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
						  .replaceAll("\r", "")
						  .replaceAll("\n", "<br>");
			} else {
				return display;
			}
		} else {
			if(cell.isEscape(page)){
				return display.replaceAll("&", "&amp;")
						  .replaceAll("<", "&lt;")
						  .replaceAll(">", "&gt;")
						  .replaceAll("\"", "&quot;")
						  .replaceAll(" ", "&nbsp;")
						  .replaceAll("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
						  .replaceAll("\r", "")
						  .replaceAll("\n", "<br>");
			} else {
				return display;
			}
		}
	}
	//渲染表单默认的value和raw，并支持外传表单默认值（优先级高于模板中的默认值）
	static FormCell renderFormCell(FormCell form,Report report) throws Exception{
		FormCell result=form.create();
		String value=result.getValue();
		String raw=result.getRaw();
		Map<String,Object> values=new HashMap<String, Object>();
		values.putAll(report.getParams());
		values.putAll(report.getDatas());
		values.put("CONSTS", report.getConsts());
		final String code=form.getCode();
		final String __code__="__"+form.getCode()+"__";
		Map<String,String> rawParams=new HashMap<String, String>();
		rawParams.putAll(report.getRawParams());
		for(String sys:Const.SYS_PARAMS){
			//避免用sys参数的历史来初始化表单的值
			rawParams.remove(sys);
		}
		if(StringUtils.isNotEmpty(value)){
			try {
				value=RenderUtil.renderString(value, values);
				result.setValue(value);
				report.getFormDefaultParams().put(code, value);
			} catch (Exception e) {
				throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].value的值",e);
			}
		}else if(rawParams.containsKey(code)){
			result.setValue(StringUtils.isNotEmpty(rawParams.get(code))?rawParams.get(code):"");
		}
		
	   if(StringUtils.isNotEmpty(raw)){
			try {
				raw=RenderUtil.renderString(raw, values);
				result.setRaw(raw);
				report.getFormDefaultParams().put(__code__, raw);
			} catch (Exception e) {
				throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].raw的值",e);
			}
		}else if(rawParams.containsKey(__code__)){
			result.setRaw(StringUtils.isNotEmpty(rawParams.get(__code__))?rawParams.get(__code__):"");
		}
		values.put(Const.NONE, code);
		if(result instanceof DateFormCell){
			SimpleDateFormat datefromat = new SimpleDateFormat("yyyy-MM-dd");
			String max=((DateFormCell) result).getMax();
			String min=((DateFormCell) result).getMin();
			if(StringUtils.isNotEmpty(max)){
				try {
					max=RenderUtil.renderString(max, values);
					datefromat.parse(max);
					((DateFormCell) result).setMax(max);
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].max的值",e);
				}
			}
			if(StringUtils.isNotEmpty(min)){
				try {
					min=RenderUtil.renderString(min, values);
					datefromat.parse(min);
					((DateFormCell) result).setMin(min);
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].min的值",e);
				}
			}
		}
		if(result instanceof NumberFormCell){
			String max=((NumberFormCell) result).getMax();
			String min=((NumberFormCell) result).getMin();
			String precision=((NumberFormCell) result).getPrecision();
			if(StringUtils.isNotEmpty(max)){
				try {
					max=RenderUtil.renderString(max, values);
					Double.valueOf(max);
					((NumberFormCell) result).setMax(max);
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].max的值",e);
				}
			}
			if(StringUtils.isNotEmpty(min)){
				try {
					min=RenderUtil.renderString(min, values);
					Double.valueOf(min);
					((NumberFormCell) result).setMin(min);
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].min的值",e);
				}
			}
			if(StringUtils.isNotEmpty(precision)){
				try {
					precision=RenderUtil.renderString(precision, values);
					Integer.valueOf(precision);
					((NumberFormCell) result).setPrecision(precision);
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].precision的值",e);
				}
			}
		}
		if(result instanceof CheckBoxFormCell){
			String text=((CheckBoxFormCell) result).getText();
			String checked=((CheckBoxFormCell) result).getChecked();
			if(StringUtils.isNotEmpty(text)){
				try {
					text=RenderUtil.renderString(text, values);
					((CheckBoxFormCell) result).setText(text);
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].text的值",e);
				}
			}
			if(StringUtils.isNotEmpty(checked)){
				try {
					checked=RenderUtil.renderString(checked, values);
					if(Boolean.valueOf(checked)){
						((CheckBoxFormCell) result).setChecked("true");
					} else {
						((CheckBoxFormCell) result).setChecked("false");
					}
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].text的值",e);
				}
			} else {
				((CheckBoxFormCell) result).setText("false");
			}
		}
		if(result instanceof CustomFormCell){
			String extra=((CustomFormCell) result).getExtra();
			if(StringUtils.isNotEmpty(extra)){
				try {
					extra=RenderUtil.renderString(extra, values);
					((CustomFormCell) result).setExtra(extra);
				} catch (Exception e) {
					throw new Exception("无法渲染表单["+NumberUtil.toExcelPoint(form.getRbegin(), form.getCbegin())+"].extra的值",e);
				}
			}
		}
		return result;
	}
}
